home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / SPIM Folder / Sources / mactext.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-08  |  25.7 KB  |  957 lines  |  [TEXT/KAHL]

  1. /* SPIM S20 MIPS simulator.
  2.    Copyright (C) 1990 by James Larus (larus@cs.wisc.edu).
  3.  
  4.    Macintosh Version by Philip Delaquess (delaques@gcg.com)
  5.    Copyright (C) 1993 by Saunders College Publishing and Morgan Kaufman Publishers.
  6.  
  7.    SPIM is free software; you can redistribute it and/or modify it
  8.    under the terms of the GNU General Public License as published by the
  9.    Free Software Foundation; either version 1, or (at your option) any
  10.    later version.
  11.  
  12.    SPIM is distributed in the hope that it will be useful, but WITHOUT
  13.    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14.    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15.    for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with GNU CC; see the file COPYING.  If not, write to James R.
  19.    Larus, Computer Sciences Department, University of Wisconsin--Madison,
  20.    1210 West Dayton Street, Madison, WI 53706, USA or to the Free
  21.    Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23.    This file copes with the scrolling text windows.
  24. */
  25.  
  26. #include <stdio.h>
  27.  
  28. #include "mactext.h"
  29. #include "spim.h"
  30. #include "inst.h"
  31. #include "mem.h"
  32. #include "reg.h"
  33.  
  34. #define MONACO 4
  35. #define COURIER 22
  36.  
  37. #define FONT MONACO
  38. #define SIZE 9
  39.  
  40. #define SESSION_LINES 100
  41. #define CONSOLE_LINES 100
  42.  
  43. SPIMTextWindow Registers, Text, Data, Console, Session, Help;
  44.  
  45. /* FormatRegisters
  46. *
  47. *  formats the register contents into the register window.
  48. */
  49.  
  50. void FormatRegisters()
  51. {
  52.     Rect box;
  53.     int i;
  54.     char buffer[256], *buffP;
  55.     char *grstr, *fpstr, *fgstr;
  56.     char *grfill, *fpfill, *fgfill;
  57.     static char *reg_names[] = {"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
  58.                                 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
  59.                                 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
  60.                                 "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
  61.  
  62.     grstr = "R%-2d (%2s): %08x  ";
  63.     fpstr = "FP%-2d: %-10.4f  ";
  64.     fgstr = "FG%-2d: %-10.4f  ";
  65.  
  66.     TESetSelect(0, 32767, Registers.text);
  67.     TEDelete(Registers.text);
  68.     buffP = buffer;
  69.     sprintf(buffP, "PC:       %08X  ", PC); buffP += strlen(buffP);
  70.     sprintf(buffP, "EPC:      %08X  ", EPC); buffP += strlen(buffP);
  71.     sprintf(buffP, "Cause:    %08X  ", Cause);  buffP += strlen(buffP);
  72.     sprintf(buffP, "BadVAddr: %08X\r", BadVAddr); buffP += strlen(buffP);
  73.     TESetSelect(32767, 32767, Registers.text);
  74.     TEInsert(buffer, buffP - buffer, Registers.text);
  75.     buffP = buffer;
  76.     sprintf(buffP, "Status:   %08X  ", Status_Reg); buffP += strlen(buffP);
  77.     sprintf(buffP, "HI:       %08X  ", HI); buffP += strlen(buffP);
  78.     sprintf(buffP, "LO:       %08X\r", LO); buffP += strlen(buffP);
  79.     TESetSelect(32767, 32767, Registers.text);
  80.     TEInsert(buffer, buffP - buffer, Registers.text);
  81.  
  82.     sprintf(buffer, "\rGeneral Registers\r");
  83.     TESetSelect(32767, 32767, Registers.text);
  84.     TEInsert(buffer, strlen(buffer), Registers.text);
  85.     for (i = 0; i < 8; ++i) {
  86.         buffP = buffer;
  87.         sprintf(buffP, grstr, i, reg_names[i], R[i]);
  88.         buffP += strlen(buffP);
  89.         sprintf(buffP, grstr, i+8, reg_names[i+8], R[i+8]);
  90.         buffP += strlen(buffP);
  91.         sprintf(buffP, grstr, i+16, reg_names[i+16], R[i+16]);
  92.         buffP += strlen(buffP);
  93.         sprintf(buffP, grstr, i+24, reg_names[i+24], R[i+24]);
  94.         buffP += strlen(buffP);
  95.         sprintf(buffP, "\r");
  96.         TESetSelect(32767, 32767, Registers.text);
  97.         TEInsert(buffer, strlen(buffer), Registers.text);
  98.     }
  99.  
  100.     sprintf(buffer, "\rDouble Floating Point Registers\r");
  101.     TESetSelect(32767, 32767, Registers.text);
  102.     TEInsert(buffer, strlen(buffer), Registers.text);
  103.     for (i = 0; i < 4; ++i) {
  104.         buffP = buffer;
  105.         sprintf(buffP, fpstr, 2*i, FGR[i]);
  106.         buffP += strlen(buffP);
  107.         sprintf(buffP, fpstr, 2*i+8, FGR[i+4]);
  108.         buffP += strlen(buffP);
  109.         sprintf(buffP, fpstr, 2*i+16, FGR[i+8]);
  110.         buffP += strlen(buffP);
  111.         sprintf(buffP, fpstr, 2*i+24, FGR[i+12]);
  112.         buffP += strlen(buffP);
  113.         sprintf(buffP, "\r");
  114.         TESetSelect(32767, 32767, Registers.text);
  115.         TEInsert(buffer, strlen(buffer), Registers.text);
  116.     }
  117.  
  118.     sprintf(buffer, "\rSingle Floating Point Registers\r");
  119.     TESetSelect(32767, 32767, Registers.text);
  120.     TEInsert(buffer, strlen(buffer), Registers.text);
  121.     for (i = 0; i < 8; i += 2) {
  122.         buffP = buffer;
  123.         sprintf(buffP, fgstr, i, FGR[i]);
  124.         buffP += strlen(buffP);
  125.         sprintf(buffP, fgstr, i+8, FGR[i+8]);
  126.         buffP += strlen(buffP);
  127.         sprintf(buffP, fgstr, i+16, FGR[i+16]);
  128.         buffP += strlen(buffP);
  129.         sprintf(buffP, fgstr, i+24, FGR[i+24]);
  130.         buffP += strlen(buffP);
  131.         sprintf(buffP, "\r");
  132.         TESetSelect(32767, 32767, Registers.text);
  133.         TEInsert(buffer, strlen(buffer), Registers.text);
  134.     }
  135. } /* FormatRegisters */
  136.  
  137. /* This formats the instructions between addresses from and to, actually drawing those
  138.    whose line numbers lie between first and last. Each line is drawn at the given
  139.    hPos and vPos, the latter of which is updated. If first is -1, all we do is count
  140.    the lines twixt from and to without bothering to draw them. If the pointer pcLine
  141.    is non-null, we supply the line number that shows the PC.
  142. */
  143.  
  144. static void formatInstructions(mem_addr from, mem_addr to,
  145.                                 int first, int last,
  146.                                 short hPos, short *vPos, int *nLines, int *pcLine)
  147. {
  148.     Boolean drawing;
  149.     instruction *inst;
  150.     char buffer[256];
  151.     mem_addr i;
  152.     char *s;
  153.  
  154.     drawing = first != -1;
  155.  
  156.     for (i = from; i < to; i += BYTES_PER_WORD) {
  157.         READ_MEM_INST(inst, i);
  158.         if ( inst != NULL ) {
  159.             if ( drawing && *nLines > last )
  160.                 return;
  161.             if ( drawing && *nLines >= first ) {
  162.                 /* prepend a label to important lines */
  163.                 if ( i == EPC )
  164.                     strcpy(buffer, "<EPC> ");
  165.                 else if ( i == R[31] )
  166.                     strcpy(buffer, "<$ra> ");
  167.                 else if ( i == PC )
  168.                     strcpy(buffer, "<PC>  ");
  169.                 else
  170.                     strcpy(buffer, "     ");
  171.                 print_inst_internal(buffer + 5, inst, i);
  172.                 for (s = buffer; *s; ++s)
  173.                     if ( *s < ' ' ) *s = ' ';
  174.                 CtoPstr(buffer);
  175.                 MoveTo(hPos, *vPos);
  176.                 DrawString(buffer);
  177.                 *vPos += SIZE + 1;
  178.             }
  179.             if ( pcLine && i == PC ) {
  180.                 *pcLine = *nLines;
  181.                 return;
  182.             }
  183.             *nLines += 1;
  184.         }
  185.     }
  186. } /* formatInstructions */
  187.  
  188. void drawText(int first, int last)
  189. {
  190.     int nLines;
  191.     GrafPtr savePort;
  192.     short hPos, vPos;
  193.  
  194.     hPos = 5 - GetCtlValue(Text.hScroll) * CharWidth(' ');
  195.     vPos = SIZE + 1;
  196.     GetPort(&savePort);
  197.     SetPort(Text.window);
  198.     nLines = 0;
  199.     formatInstructions(TEXT_BOT, text_top, first, last, hPos, &vPos, &nLines, 0);
  200.     if ( nLines >= first && nLines <= last )
  201.         vPos += SIZE + 1;
  202.     nLines += 1;
  203.     if ( nLines >= first && nLines <= last ) {
  204.         MoveTo(hPos, vPos);
  205.         DrawString("\pKERNEL TEXT");
  206.         vPos += SIZE + 1;
  207.     }
  208.     nLines += 1;
  209.     formatInstructions(K_TEXT_BOT, k_text_top, first, last, hPos, &vPos, &nLines, 0);
  210.  
  211.     SetPort(savePort);
  212. } /* drawText */
  213.  
  214. void CountText()
  215. {
  216.     int nLines;
  217.     short vPos;
  218.     GrafPtr savePort;
  219.  
  220.     nLines = 0;
  221.     formatInstructions(TEXT_BOT, text_top, -1, -1, 0, &vPos, &nLines, 0);
  222.     nLines += 2; /* KERNEL TEXT label */
  223.     formatInstructions(K_TEXT_BOT, k_text_top, -1, -1, 0, &vPos, &nLines, 0);
  224.  
  225.     /* touch the vertical scroll */
  226.     SetCtlMax(Text.vScroll, nLines);
  227.  
  228.     /* force a redraw */
  229.     GetPort(&savePort);
  230.     SetPort(Text.window);
  231.     InvalRect(&(Text.window->portRect));
  232.     SetPort(savePort);
  233. } /* CountText */
  234.  
  235. /* This is called every time SPIM stops running. It makes sure that the PC line is
  236.    scrolled into view (provided that the PC is in one of the text segments).
  237. */
  238.  
  239. void RecenterText()
  240. {
  241.     GrafPtr savePort;
  242.     Rect inval;
  243.     short vPos;
  244.     int nLines, pcLine;
  245.  
  246.     /* find the line number showing the stupid PC */
  247.     pcLine = -1;
  248.     nLines = 0;
  249.     formatInstructions(TEXT_BOT, text_top, -1, -1, 0, &vPos, &nLines, &pcLine);
  250.     if ( pcLine == -1 )
  251.         formatInstructions(K_TEXT_BOT, k_text_top, -1, -1, 0, &vPos, &nLines, &pcLine);
  252.  
  253.     /* if the PC is in a text seg, scroll so that it's visible */
  254.     if ( pcLine != -1 ) {
  255.         if ( pcLine < Text.linesPerPage )
  256.             SetCtlValue(Text.vScroll, 0);
  257.         else if ( pcLine < GetCtlValue(Text.vScroll) )
  258.             SetCtlValue(Text.vScroll, pcLine - Text.linesPerPage / 2);
  259.         else if ( pcLine > GetCtlValue(Text.vScroll) + Text.linesPerPage )
  260.             SetCtlValue(Text.vScroll, pcLine - Text.linesPerPage / 2);
  261.     }
  262.  
  263.     /* but, alas! always force a redraw because a $break instruction might have snuck in */
  264.     GetPort(&savePort);
  265.     SetPort(Text.window);
  266.     inval = Text.window->portRect;
  267.     inval.bottom -= 16;
  268.     inval.right -= 16;
  269.     InvalRect(&inval);
  270.     SetPort(savePort);
  271. } /* RecenterText */
  272.  
  273. /* This figures out how to draw a display of a data segment. */
  274.  
  275. static void formatValues(mem_addr from, mem_addr to,
  276.                             int first, int last,
  277.                             short hPos, short *vPos, int *nLines)
  278. {
  279.     mem_addr i, j;
  280.     mem_word val;
  281.     Boolean drawing;
  282.     char buffer[256], *bufP;
  283.     int line;
  284.  
  285.     drawing = first != -1;
  286.  
  287.     for (i = from & 0xfffffffc; i < to; i += BYTES_PER_WORD) {
  288.         /* look for a block of four or more zero memory words */
  289.         for (j = 0; i + j < to; j += BYTES_PER_WORD) {
  290.             READ_MEM_WORD(val, i + j);
  291.             if ( val != 0 )
  292.                 break;
  293.         }
  294.         if ( (i + j < to) && (j != 0) )
  295.             j -= BYTES_PER_WORD;
  296.         if ( j >= 4 * BYTES_PER_WORD ) {
  297.             if ( drawing && *nLines > last )
  298.                 return;
  299.             if ( drawing && *nLines >= first ) {
  300.                 sprintf(buffer, "[0x%08x]...[0x%08x]    0x00000000", i, i + j);
  301.                 CtoPstr(buffer);
  302.                 MoveTo(hPos, *vPos);
  303.                 DrawString(buffer);
  304.                 *vPos += SIZE + 1;
  305.             }
  306.             *nLines += 1;
  307.             i += j;
  308.             continue;
  309.         }
  310.         /* otherwise print the next four words on one line */
  311.         if ( drawing && *nLines >= first && *nLines <= last ) {
  312.             sprintf(buffer, "[0x%08x]    0x%08x", i, val);
  313.             bufP = buffer + strlen(buffer);
  314.         }
  315.         for ( ; i % 16 != 12; ) {
  316.             i += BYTES_PER_WORD;
  317.             if ( drawing && *nLines >= first ) {
  318.                 READ_MEM_WORD(val, i);
  319.                 sprintf(bufP, "  0x%08x", val);
  320.                 bufP += 12;
  321.             }
  322.         }
  323.         if ( drawing && *nLines > last )
  324.             return;
  325.         if ( drawing && *nLines >= first ) {
  326.             CtoPstr(buffer);
  327.             MoveTo(hPos, *vPos);
  328.             DrawString(buffer);
  329.             *vPos += SIZE + 1;
  330.         }
  331.         *nLines += 1;
  332.     }
  333. } /* formatValues */
  334.  
  335. void drawData(int first, int last)
  336. {
  337.     int nLines;
  338.     GrafPtr savePort;
  339.     short hPos, vPos;
  340.  
  341.     hPos = 5 - GetCtlValue(Data.hScroll) * CharWidth(' ');
  342.     vPos = SIZE + 1;
  343.     GetPort(&savePort);
  344.     SetPort(Data.window);
  345.     if ( first == 0 ) {
  346.         MoveTo(hPos, vPos);
  347.         DrawString("\pDATA");
  348.         vPos += SIZE + 1;
  349.     }
  350.     nLines = 1;
  351.     formatValues(DATA_BOT, data_top, first, last, hPos, &vPos, &nLines);
  352.     if ( nLines >= first && nLines <= last )
  353.         vPos += SIZE + 1;
  354.     nLines += 1;
  355.     if ( nLines >= first && nLines <= last ) {
  356.         MoveTo(hPos, vPos);
  357.         DrawString("\pSTACK");
  358.         vPos += SIZE + 1;
  359.     }
  360.     nLines += 1;
  361.     formatValues(R[29] - BYTES_PER_WORD, STACK_TOP, first, last, hPos, &vPos, &nLines);
  362.     if ( nLines >= first && nLines <= last )
  363.         vPos += SIZE + 1;
  364.     nLines += 1;
  365.     if ( nLines >= first && nLines <= last ) {
  366.         MoveTo(hPos, vPos);
  367.         DrawString("\pKERNEL DATA");
  368.         vPos += SIZE + 1;
  369.     }
  370.     nLines += 1;
  371.     formatValues(K_DATA_BOT, k_data_top, first, last, hPos, &vPos, &nLines);
  372.  
  373.     SetPort(savePort);
  374. } /* drawData */
  375.  
  376. /* This figures out how many lines of type are needed to display the data segments.
  377.    It also sets the vertical scroll parameters and forces a redraw.
  378.    We call it after initialization, after loading a file, and after running the CPU.
  379. */
  380.  
  381. void CountData()
  382. {
  383.     int nLines;
  384.     short vPos;
  385.     GrafPtr savePort;
  386.  
  387.     /* count the data lines */
  388.     nLines = 1; /* DATA label */
  389.     formatValues(DATA_BOT, data_top, -1, -1, 0, &vPos, &nLines);
  390.     nLines += 2; /* STACK label */
  391.     formatValues(R[29] - BYTES_PER_WORD, STACK_TOP, -1, -1, 0, &vPos, &nLines);
  392.     nLines += 2; /* KERNEL DATA label */
  393.     formatValues(K_DATA_BOT, k_data_top, -1, -1, 0, &vPos, &nLines);
  394.  
  395.     /* touch the vertical scroll */
  396.     SetCtlMax(Data.vScroll, nLines);
  397.  
  398.     /* force data redraw */
  399.     GetPort(&savePort);
  400.     SetPort(Data.window);
  401.     InvalRect(&(Data.window->portRect));
  402.     SetPort(savePort);
  403. } /* CountData */
  404.  
  405. static void makeWindow(Str255 title, SPIMTextWindow *stWindow, Boolean useTE)
  406. {
  407.     Rect r, view, dest;
  408.     static short nextHPos = 5, nextVPos = 45;
  409.  
  410.     stWindow->window = GetNewWindow(128, 0, (WindowPtr) -1);
  411.     MoveWindow(stWindow->window, nextHPos, nextVPos, FALSE);
  412.     SetWTitle(stWindow->window, title);
  413.     SetWRefCon(stWindow->window, (long) stWindow);
  414.     SetPort(stWindow->window);
  415.     TextFont(FONT);
  416.     TextSize(SIZE);
  417.     r = stWindow->window->portRect;
  418.  
  419.     if ( useTE ) {
  420.         SetRect(&dest, 5, 5, 2000, 2000);
  421.         view = r;
  422.         view.left += 5;
  423.         view.top += 5;
  424.         view.bottom -= 15;
  425.         view.right -= 15;
  426.         if ( stWindow == &Help )
  427.             stWindow->text = TENew(&view, &view);
  428.         else
  429.             stWindow->text = TENew(&dest, &view);
  430.         stWindow->linesPerPage = (r.bottom - r.top - 20);
  431.         stWindow->linesPerPage /= (*(stWindow->text))->lineHeight;
  432.     }
  433.     else {
  434.         stWindow->text = (TEHandle) 0;
  435.         stWindow->linesPerPage = (r.bottom - r.top - 20) / (SIZE + 1);
  436.     }
  437.     stWindow->columnsPerPage = (r.right - r.left - 20) / CharWidth(' ');
  438.  
  439.     stWindow->vScroll = GetNewControl(128, stWindow->window);
  440.     MoveControl(stWindow->vScroll, r.right - 15, -1);
  441.     SizeControl(stWindow->vScroll, 16, r.bottom - 13);
  442.     SetCRefCon(stWindow->vScroll, 1L);
  443.  
  444.     stWindow->hScroll = GetNewControl(128, stWindow->window);
  445.     MoveControl(stWindow->hScroll, -1, r.bottom - 15);
  446.     SizeControl(stWindow->hScroll, r.right - 13, 16);
  447.     SetCRefCon(stWindow->hScroll, 0L);
  448.     SetCtlMin(stWindow->hScroll, 0);
  449.     SetCtlValue(stWindow->hScroll, 0);
  450.     SetCtlMax(stWindow->hScroll, 256);
  451.  
  452.     nextHPos += 5;
  453.     nextVPos += 20;
  454. } /* makeWindow */
  455.  
  456. static void loadHelpText()
  457. {
  458.     short refNum;
  459.     long fileSize;
  460.     OSErr err;
  461.     Handle text;
  462.  
  463.     err = FSOpen("\phelp.text", (short) 0, &refNum);
  464.     if ( err != noErr ) return;
  465.     err = GetEOF(refNum, &fileSize);
  466.     if ( err != noErr ) return;
  467.     text = NewHandle(fileSize);
  468.     if ( !text ) return;
  469.     HLock(text);
  470.     err = SetFPos(refNum, fsFromStart, 0L);
  471.     if ( err != noErr ) return;
  472.     err = FSRead(refNum, &fileSize, *text);
  473.     if ( err != noErr ) return;
  474.     FSClose(refNum);
  475.  
  476.     TESetText(*text, fileSize, Help.text);
  477.     HUnlock(text);
  478.     DisposHandle(text);
  479.  
  480.     SetCtlMax(Help.vScroll, (*(Help.text))->nLines - Help.linesPerPage);
  481. } /* loadHelpText */
  482.  
  483. void InitTextWindows()
  484. {
  485.     makeWindow("\pSPIM/SAL Registers", &Registers, TRUE);
  486.     makeWindow("\pSPIM/SAL Text Segments", &Text, FALSE);
  487.     makeWindow("\pSPIM/SAL Data Segments", &Data, FALSE);
  488.     makeWindow("\pSPIM/SAL Session", &Session, TRUE);
  489.     makeWindow("\pSPIM/SAL Console", &Console, TRUE);
  490.  
  491.     makeWindow("\pSPIM/SAL Help", &Help, TRUE);
  492.     loadHelpText();
  493.  
  494.     SetCtlMax(Registers.vScroll, 28);
  495.     SetCtlMax(Session.vScroll, SESSION_LINES);
  496.     SetCtlMax(Console.vScroll, CONSOLE_LINES);
  497. } /* InitTextWindows */
  498.  
  499. void SelectTextWindow(SPIMTextWindow *textWindow)
  500. {
  501.     ShowWindow(textWindow->window);
  502.     SelectWindow(textWindow->window);
  503. } /* SelectTextWindow */
  504.  
  505. void ActivateTextWindow(WindowPtr window, Boolean active)
  506. {
  507.     SPIMTextWindow *stWindow;
  508.  
  509.     stWindow = (SPIMTextWindow *) GetWRefCon(window);
  510.  
  511.     DrawGrowIcon(window);
  512.     HiliteControl(stWindow->hScroll, active ? 0 : 255);
  513.     HiliteControl(stWindow->vScroll, active ? 0 : 255);
  514.     if ( active )
  515.         SetPort(window);
  516. } /* ActivateTextWindow */
  517.  
  518. void UpdateTextWindow(WindowPtr window)
  519. {
  520.     SPIMTextWindow *stWindow;
  521.     GrafPtr savePort;
  522.     Rect r;
  523.     short first;
  524.  
  525.     stWindow = (SPIMTextWindow *) GetWRefCon(window);
  526.  
  527.     BeginUpdate(window);
  528.     GetPort(&savePort);
  529.     SetPort(window);
  530.     r = window->portRect;
  531.     EraseRect(&r);
  532.     if ( stWindow->text ) {
  533.         r.right -= 16;
  534.         r.bottom -= 16;
  535.         TEUpdate(&r, stWindow->text);
  536.     }
  537.     else if ( stWindow == &Data ) {
  538.         first = GetCtlValue(Data.vScroll);
  539.         drawData(first, first + Data.linesPerPage);
  540.     }
  541.     else if ( stWindow == &Text ) {
  542.         first = GetCtlValue(Text.vScroll);
  543.         drawText(first, first + Text.linesPerPage);
  544.     }
  545.     DrawGrowIcon(window);
  546.     DrawControls(window);
  547.     SetPort(savePort);
  548.     EndUpdate(window);
  549. } /* UpdateTextWindow */
  550.  
  551. /* This repositions the controls and resets the arithmetic of a window whose size
  552.    has just been changed.
  553. */
  554.  
  555. void RecalculateTextWindow(WindowPtr window)
  556. {
  557.     GrafPtr savePort;
  558.     SPIMTextWindow *stWindow;
  559.     Rect inval, view, r;
  560.  
  561.     stWindow = (SPIMTextWindow *) GetWRefCon(window);
  562.  
  563.     GetPort(&savePort);
  564.     SetPort(window);
  565.     r = window->portRect;
  566.     ClipRect(&r);
  567.     SetPort(savePort);
  568.     MoveControl(stWindow->vScroll, r.right - 15, -1);
  569.     SizeControl(stWindow->vScroll, 16, r.bottom - 13);
  570.     MoveControl(stWindow->hScroll, -1, r.bottom - 15);
  571.     SizeControl(stWindow->hScroll, r.right - 13, 16);
  572.     if ( stWindow->text ) {
  573.         view = r;
  574.         view.left += 5;
  575.         view.top += 5;
  576.         view.bottom -= 15;
  577.         view.right -= 15;
  578.         (*(stWindow->text))->viewRect = view;
  579.         stWindow->linesPerPage = (r.bottom - r.top - 20);
  580.         stWindow->linesPerPage /= (*(stWindow->text))->lineHeight;
  581.     }
  582.     else
  583.         stWindow->linesPerPage = (r.bottom - r.top - 20) / (SIZE + 1);
  584.     stWindow->columnsPerPage = (r.right - r.left - 20) / CharWidth(' ');
  585. } /* RecalculateTextWindow */
  586.  
  587. void SizeTextWindow(WindowPtr window, Point start)
  588. {
  589.     SPIMTextWindow *stWindow;
  590.     Rect limits, inval;
  591.     long newSize;
  592.  
  593.     stWindow = (SPIMTextWindow *) GetWRefCon(window);
  594.  
  595.     SetRect(&limits, 100, 100, 1000, 1000);
  596.     newSize = GrowWindow(window, start, &limits);
  597.     if ( newSize ) {
  598.         inval = window->portRect;
  599.         inval.left = inval.right - 16;
  600.         inval.top = inval.bottom - 16;
  601.         InvalRect(&inval);
  602.         SizeWindow(window, LoWord(newSize), HiWord(newSize), TRUE);
  603.         RecalculateTextWindow(window);
  604.     }
  605.         
  606. } /* SizeTextWindow */
  607.  
  608. void TileTextWindows(Boolean vertically)
  609. {
  610.     short nOpen, total, width, height, i, h, v;
  611.     WindowPtr openOnes[5];
  612.  
  613.     nOpen = 0;
  614.     if ( ((WindowPeek) (Registers.window))->visible )
  615.         openOnes[nOpen++] = Registers.window;
  616.     if ( ((WindowPeek) (Text.window))->visible )
  617.         openOnes[nOpen++] = Text.window;
  618.     if ( ((WindowPeek) (Data.window))->visible )
  619.         openOnes[nOpen++] = Data.window;
  620.     if ( ((WindowPeek) (Session.window))->visible )
  621.         openOnes[nOpen++] = Session.window;
  622.     if ( ((WindowPeek) (Console.window))->visible )
  623.         openOnes[nOpen++] = Console.window;
  624.  
  625.     if ( nOpen == 0 )
  626.         return;
  627.  
  628.     h = 5; v = 45;
  629.     if ( vertically ) {
  630.         total = screenBits.bounds.bottom - 25;
  631.         height = total / nOpen - 5;
  632.         width = screenBits.bounds.right - 10;
  633.         for (i = 0; i < nOpen; ++i) {
  634.             MoveWindow(openOnes[i], h, v, FALSE);
  635.             SizeWindow(openOnes[i], width, height - 20, TRUE);
  636.             RecalculateTextWindow(openOnes[i]);
  637.             v += height + 5;
  638.         }
  639.     }
  640.     else {
  641.         total = screenBits.bounds.right - 5;
  642.         width = total / nOpen - 5;
  643.         height = screenBits.bounds.bottom - 50;
  644.         for (i = 0; i < nOpen; ++i) {
  645.             MoveWindow(openOnes[i], h, v, FALSE);
  646.             SizeWindow(openOnes[i], width, height, TRUE);
  647.             RecalculateTextWindow(openOnes[i]);
  648.             h += width + 5;
  649.         }
  650.     }
  651. } /* TileTextWindows */
  652.  
  653. /* Action procs for the scroll bars. */
  654.  
  655. static short scrollAmount;
  656. static SPIMTextWindow *scrollWindow;
  657. static RgnHandle scrollRgn;
  658.  
  659. static pascal void verticalAction(ControlHandle ctrl, short partCode)
  660. {
  661.     short delta, value;
  662.     Rect scroll;
  663.  
  664.     delta = GetCtlValue(ctrl);
  665.     SetCtlValue(ctrl, scrollAmount + GetCtlValue(ctrl));
  666.     value = GetCtlValue(ctrl);
  667.     delta -= value;
  668.     if ( delta ) {
  669.         /* value actually changed */
  670.         if ( scrollWindow->text ) {
  671.             delta *= (*(scrollWindow->text))->lineHeight;
  672.             TEScroll(0, delta, scrollWindow->text);
  673.         }
  674.         else {
  675.             /* redraw text or data */
  676.             scroll = scrollWindow->window->portRect;
  677.             scroll.right -= 15;
  678.             scroll.bottom -= 15;
  679.             ScrollRect(&scroll, 0, delta * (SIZE + 1), scrollRgn);
  680.             ClipRect(&scroll);
  681.             if ( scrollWindow == &Text )
  682.                 drawText(value, value + Text.linesPerPage);
  683.             else
  684.                 drawData(value, value + Data.linesPerPage);
  685.             ClipRect(&(scrollWindow->window->portRect));
  686.         }
  687.     }
  688. } /* verticalAction */
  689.  
  690. static pascal void horizontalAction(ControlHandle ctrl, short partCode)
  691. {
  692.     short delta, value, vPos;
  693.     Rect scroll;
  694.  
  695.     delta = GetCtlValue(ctrl);
  696.     SetCtlValue(ctrl, scrollAmount + GetCtlValue(ctrl));
  697.     value = GetCtlValue(ctrl);
  698.     delta -= value;
  699.     if ( delta ) {
  700.         /* value actually changed */
  701.         if ( scrollWindow->text ) {
  702.             delta *= CharWidth(' ');
  703.             TEScroll(delta, 0, scrollWindow->text);
  704.         }
  705.         else {
  706.             /* redraw text or data */
  707.             scroll = scrollWindow->window->portRect;
  708.             scroll.right -= 15;
  709.             scroll.bottom -= 15;
  710.             ScrollRect(&scroll, delta * CharWidth(' '), 0, scrollRgn);
  711.             ClipRect(&scroll);
  712.             vPos = GetCtlValue(scrollWindow->vScroll);
  713.             if ( scrollWindow == &Text )
  714.                 drawText(vPos, vPos + Text.linesPerPage);
  715.             else
  716.                 drawData(vPos, vPos + Data.linesPerPage);
  717.             ClipRect(&(scrollWindow->window->portRect));
  718.         }
  719.     }
  720. } /* horizontalAction */
  721.  
  722. /* This checks for mouse action in the scroll bars. */
  723.  
  724. void ClickTextWindow(WindowPtr window, EventRecord *event)
  725. {
  726.     SPIMTextWindow *stWindow;
  727.     ControlHandle ctrl;
  728.     short code, value, delta;
  729.     Boolean vertical;
  730.     Rect inval;
  731.  
  732.     stWindow = (SPIMTextWindow *) GetWRefCon(window);
  733.     scrollWindow = stWindow;
  734.  
  735.     GlobalToLocal(&(event->where));
  736.     code = FindControl(event->where, window, &ctrl);
  737.     if ( code )
  738.         vertical = GetCRefCon(ctrl) ? TRUE : FALSE;
  739.     else
  740.         return;
  741.  
  742.     scrollRgn = NewRgn();
  743.     switch ( code ) {
  744.     case inUpButton :
  745.         scrollAmount = -1;
  746.         TrackControl(ctrl, event->where, vertical ? verticalAction : horizontalAction);
  747.         break;
  748.     case inDownButton :
  749.         scrollAmount = 1;
  750.         TrackControl(ctrl, event->where, vertical ? verticalAction : horizontalAction);
  751.         break;
  752.     case inPageUp :
  753.         if ( vertical )
  754.             scrollAmount = -(stWindow->linesPerPage - 2);
  755.         else
  756.             scrollAmount = -(stWindow->columnsPerPage - 2);
  757.         TrackControl(ctrl, event->where, vertical ? verticalAction : horizontalAction);
  758.         break;
  759.     case inPageDown :
  760.         if ( vertical )
  761.             scrollAmount = stWindow->linesPerPage - 2;
  762.         else
  763.             scrollAmount = stWindow->columnsPerPage - 2;
  764.         TrackControl(ctrl, event->where, vertical ? verticalAction : horizontalAction);
  765.         break;
  766.     case inThumb :
  767.         delta = GetCtlValue(ctrl);
  768.         TrackControl(ctrl, event->where, 0);
  769.         value = GetCtlValue(ctrl);
  770.         delta -= value;
  771.         if ( delta ) {
  772.             if ( stWindow->text ) {
  773.                 delta *= vertical ? (*(stWindow->text))->lineHeight : CharWidth(' ');
  774.                 TEScroll(vertical ? 0 : delta, vertical ? delta : 0, stWindow->text);
  775.             }
  776.             else {
  777.                 inval = window->portRect;
  778.                 inval.right -= 15;
  779.                 inval.bottom -= 15;
  780.                 InvalRect(&inval);
  781.             }
  782.         }
  783.         break;
  784.     }
  785.     DisposeRgn(scrollRgn);
  786. } /* ClickTextWindow */
  787.  
  788. /* This forces the last line in the TE buff to be visible. */
  789.  
  790. static void scrollIntoView(SPIMTextWindow *stWindow)
  791. {
  792.     short nLines, height, value, delta;
  793.  
  794.     nLines = (*(stWindow->text))->nLines;
  795.     height = (*(stWindow->text))->lineHeight;
  796.     value = GetCtlValue(stWindow->vScroll);
  797.     if ( nLines < stWindow->linesPerPage ) {
  798.         /* scroll all the way back to zero */
  799.         SetCtlValue(stWindow->vScroll, 0);
  800.         delta = GetCtlValue(stWindow->vScroll) - value;
  801.         TEScroll(0, -delta * height, stWindow->text);
  802.     }
  803.     else if ( nLines < value ) {
  804.         /* scroll up so last line is in middle of screen */
  805.         SetCtlValue(stWindow->vScroll, nLines - stWindow->linesPerPage / 2);
  806.         delta = GetCtlValue(stWindow->vScroll) - value;
  807.         TEScroll(0, -delta * height, stWindow->text);
  808.     }
  809.     else if ( nLines > value + stWindow->linesPerPage ) {
  810.         /* scroll down so last line is in middle of screen */
  811.         SetCtlValue(stWindow->vScroll, nLines - stWindow->linesPerPage / 2);
  812.         delta = GetCtlValue(stWindow->vScroll) - value;
  813.         TEScroll(0, -delta * height, stWindow->text);
  814.     }
  815. } /* scrollIntoView */
  816.  
  817. /* This writes output to either the Session or the Console window.
  818.    As a sanity check, I restrict the number of lines of output that can be produced
  819.    while assembling a source file.
  820. */
  821.  
  822. Boolean Assembling = FALSE;
  823. short MessageCount;
  824.  
  825. #define MAX_MESSAGES (SESSION_LINES - 10)
  826.  
  827. void write_output(long whither, char *buf)
  828. {
  829.     SPIMTextWindow *stWindow;
  830.     char format[256], *ptr;
  831.     short maxLines, excess, end;
  832.     int i;
  833.  
  834.     if ( Assembling && (MessageCount >= MAX_MESSAGES) )
  835.         return;
  836.  
  837.     /* convert '\n' to '\r', expand tabs */
  838.     i = 0;
  839.     while ( *buf ) {
  840.         if ( *buf == '\n' ) {
  841.             ++MessageCount;
  842.             format[i++] = '\r';
  843.         }
  844.         else if ( *buf == '\t' ) {
  845.             do
  846.                 format[i++] = ' ';
  847.             while ( i % 8 );
  848.         }
  849.         else
  850.             format[i++] = *buf;
  851.         ++buf;
  852.     }
  853.     format[i] = '\0';
  854.  
  855.     if ( Assembling && (MessageCount >= MAX_MESSAGES) ) {
  856.         ptr = "\r\r*** Too many errors! ***\r";
  857.         i = strlen(ptr);
  858.     }
  859.     else
  860.         ptr = format;
  861.  
  862.     stWindow = (SPIMTextWindow *) whither;
  863.     maxLines = stWindow == &Session ? SESSION_LINES : CONSOLE_LINES;
  864.     TESetSelect(32767, 32767, stWindow->text);
  865.     TEInsert(ptr, i, stWindow->text);
  866.     excess = (*(stWindow->text))->nLines - maxLines;
  867.     if ( excess > 0 ) {
  868.         end = (*(stWindow->text))->lineStarts[excess];
  869.         TESetSelect(0, end, stWindow->text);
  870.         TEDelete(stWindow->text);
  871.     }
  872.     scrollIntoView(stWindow);
  873.     ShowWindow(stWindow->window);
  874. } /* write_output */
  875.  
  876. /* This gets a string from the Console to supply a system call. */
  877.  
  878. void read_input(char *buffer, int length)
  879. {
  880.     extern DialogPtr RunningDlg;
  881.     Point endPoint;
  882.     Rect teRect;
  883.     TEHandle teH;
  884.     Boolean reading;
  885.     EventRecord event;
  886.     char typed;
  887.     WindowPtr window;
  888.  
  889.     /* bring the Console window to the top */
  890.     ShowWindow(Console.window);
  891.     SelectWindow(Console.window);
  892.     SetPort(Console.window);
  893.     scrollIntoView(&Console);
  894.  
  895.     /* figure out where the input TE rectangle goes */
  896.     endPoint = TEGetPoint(32767, Console.text);
  897.     teRect.left = endPoint.h;
  898.     teRect.right = Console.window->portRect.right - 15;
  899.     teRect.bottom = endPoint.v + 4;
  900.     teRect.top = teRect.bottom - (*(Console.text))->lineHeight - 4;
  901.     teH = TENew(&teRect, &teRect);
  902.     TEActivate(teH);
  903.  
  904.     /* fetch and handle events */
  905.     reading = TRUE;
  906.     while ( reading ) {
  907.         TEIdle(teH);
  908.         GetNextEvent(everyEvent, &event);
  909.         switch ( event.what ) {
  910.         case activateEvt :
  911.             if ( event.message == (long) Console.window )
  912.                 ActivateTextWindow(Console.window, TRUE);
  913.             break;
  914.         case updateEvt :
  915.             if ( event.message == (long) Console.window )
  916.                 UpdateTextWindow(Console.window);
  917.             break;
  918.         case keyDown :
  919.             typed = event.message & charCodeMask;
  920.             if ( typed == '\r' ) {
  921.                 reading = FALSE;
  922.                 typed = '\n';
  923.             }
  924.             TEKey(typed, teH);
  925.             break;
  926.         case mouseDown :
  927.             if ( FindWindow(event.where, &window) == inContent ) {
  928.                 if ( window == Console.window ) {
  929.                     GlobalToLocal(&event.where);
  930.                     if ( PtInRect(event.where, &teRect) )
  931.                         TEClick(event.where, event.modifiers & shiftKey ? TRUE : FALSE,
  932.                                 teH);
  933.                 }
  934.             }
  935.             break;
  936.         default :
  937.             break;
  938.         }
  939.     }
  940.  
  941.     /* get the string and echo it */
  942.     strncpy(buffer, *((*teH)->hText), length);
  943.     buffer[(*teH)->teLength] = '\0';
  944.     write_output(console_out, buffer);
  945.  
  946.     /* put the running dialog back on top */
  947.     TEDispose(teH);
  948.     SelectWindow(RunningDlg);
  949. } /* read_input */
  950.  
  951. void ClearConsole()
  952. {
  953.     TESetSelect(0, 32767, Console.text);
  954.     TEDelete(Console.text);
  955.     scrollIntoView(&Console);
  956. }
  957.